package com.example.securityproject.demos.config;

import com.alibaba.fastjson.JSON;
import com.example.securityproject.demos.common.CommonResult;
import com.example.securityproject.demos.filter.TokenAuthenticationFilter;
import com.example.securityproject.demos.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author Jiangjb
 * @Date 2024/4/7
 * @Description SpringSecurity安全框架配置
 */
@Configuration
//开启Spring Security的功能
@EnableWebSecurity
//prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {

    @Resource
//    @Lazy
    private CustomUserDetailsService customerUserDetailsService;

    /**
     * 指定加密方式
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 允许匿名访问的地址
     */
    @Autowired
    private PermitAllUrlProperties permitAllUrl;

    @Resource
    private TokenAuthenticationFilter tokenAuthenticationFilter;

    /**
     * 一些简单的配置
     */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        // 禁用session
//        http.sessionManagement().disable();
//        http.csrf().disable();
//        http.formLogin()
//                // 自定义登录成功后路径
//                .defaultSuccessUrl("/test/helloworld")
//                // 自定义登录路径
//                .loginProcessingUrl("/login");
//        // 可以匿名访问
//        http.authorizeRequests()
//                .antMatchers("/login").anonymous()
//                .anyRequest().authenticated();
//    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //解决前端跨域问题
        http.cors().and().headers().frameOptions().disable();

        // 注解标记允许匿名访问的url，例如登录、注册等接口
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 除匿名外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                // 认证异常处理handler 使用的 lambda的内部的实现
                .authenticationEntryPoint((request, response, authenticationException) -> {
                    CommonResult restErrorResponse = new CommonResult(HttpServletResponse.SC_UNAUTHORIZED, "尚未认证！");//自定义返回类
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.getWriter().println(JSON.toJSONString(restErrorResponse));

                })
                // 授权异常处理handler 使用的 lambda的内部的实现
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    CommonResult restErrorResponse = new CommonResult(HttpServletResponse.SC_FORBIDDEN, "没有权限！");//自定义返回类
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.getWriter().println(JSON.toJSONString(restErrorResponse));
                });
        http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

    }

//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        //解决前端跨域问题
//        http.cors().and().headers().frameOptions().disable();
//        http
//                //1、配置权限认证
//                .authorizeRequests()
////                .antMatchers("/login").permitAll()
//                .antMatchers("/login").permitAll()
//                .anyRequest() //任何其它请求
//                .authenticated() //都需要身份认证
//                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
//                    public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
//                        fsi.setAccessDecisionManager(accessDecisionManager);
//                        fsi.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
//                        return fsi;
//                    }
//                })
//                .and().addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, tUserService))                    // 增加两个过滤器 职责链模式
//                .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager)).httpBasic()
//                .and()
//                //2、登录配置表单认证方式
//                .formLogin()
////                .loginProcessingUrl("/login") // 自定义登录URL
////                .permitAll()
//                .usernameParameter("username")//设置登录账号参数，与表单参数一致
//                .passwordParameter("password")//设置登录密码参数，与表单参数一致
////                .successHandler(loginSuccessHandler)//使用自定义的成功结果处理器
////                .failureHandler(loginFailureHandler)//使用自定义失败的结果处理器
//                .and()
//                //3、注销
////                .logout()
////                .logoutUrl("/logout")
////                .logoutSuccessHandler(new CustomLogoutSuccessHandler())
////                .permitAll()
////                .and()
//                //4、session管理
////                .sessionManagement()
////                .invalidSessionUrl("/login") //失效后跳转到登陆页面
//                //单用户登录，如果有一个登录了，同一个用户在其他地方登录将前一个剔除下线
//                //.maximumSessions(1).expiredSessionStrategy(expiredSessionStrategy())
//                //单用户登录，如果有一个登录了，同一个用户在其他地方不能登录
//                //.maximumSessions(1).maxSessionsPreventsLogin(true) ;
////                .and()
//                //5、禁用跨站csrf攻击防御
//                .csrf()
//                .disable();
//    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customerUserDetailsService).passwordEncoder(passwordEncoder());
    }

    /*
     * @description:
     *  ignore是完全绕过了spring security的所有filter，相当于不走spring security。
     *  permitall没有绕过spring security，其中包含了登录的以及匿名的。
     * @author: Jiangjb
     * @date: 2024/4/11 19:40
     **/
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/fonts/**");
    }
}
